import json
import os
import re
from optparse import Option

import xlsxwriter
import io  # 用于在内存中创建文件
import ast
import asyncio
# import textract
import tempfile
import time
# import markdown
# import uuid
import shutil
import zipfile
from PyPDF2 import PdfReader
from tempfile import NamedTemporaryFile
from datetime import datetime, timedelta
from uuid import uuid4
from typing import Union, List, Dict, Any
from docxtpl import DocxTemplate
from urllib.parse import urlparse
import requests
from fastapi import UploadFile, BackgroundTasks

from bisheng.api.jsj_api.model.metro_fault import (MetroFault, MetroFaultDao, EquipmentBase,
                                                   LevelEnum, ConfigDao, EquipmentDao)
from bisheng.api.jsj_api.model.request_record import RequestRecordDao, RequestRecord
from bisheng.api.jsj_api.model.alarm_record import AlarmRecordDao, AlarmRecord
from bisheng.api.jsj_api.model.metro_fault_task import MetroFaultTaskDao, StatusEnum
from bisheng.api.jsj_api.model.fault_work_order import FaultWorkOrder, FaultWorkOrderDao
from bisheng.api.jsj_api.model.fault_work_ticket import FaultWorkTicket, FaultWorkTicketDao
from bisheng.api.jsj_api.model.fault_work_plan import FaultWorkPlan, FaultWorkPlanDao
from bisheng.api.jsj_api.model.fault_work_repair import FaultWorkRepair, FaultWorkRepairDao
from bisheng.api.jsj_api.model.equipment_info import EquipmentInfo, EquipmentInfoDao
from bisheng.api.jsj_api.model.schedule_record import ScheduleRecord, ScheduleRecordDao
from bisheng.api.jsj_api.model.alarm_status import AlarmStatusDao, AlarmStatus
from .task_service import TaskService
from .equipment_service import MaintenanceRuleService, EquipmentService
from .utils.simple_minio_client import SimpleMinioClient


# from docx import Document
# from docx.shared import Pt
# from docx.enum.style import WD_STYLE_TYPE


class MetroFaultService:
    def __init__(self):
        current_date = datetime.now().strftime("%Y-%m-%d")
        self.data_time_value = current_date
        self.get_current_date()

    @staticmethod
    def download_file(url: str) -> UploadFile:
        """
        从给定的URL下载文件并返回一个UploadFile对象

        :param url: 要下载的文件的URL
        :return: UploadFile对象
        """
        response = requests.get(url, stream=True)
        response.raise_for_status()  # 如果请求不成功则抛出异常

        # 创建一个命名的临时文件
        with NamedTemporaryFile(delete=False) as temp_file:
            # 写入文件内容
            for chunk in response.iter_content(chunk_size=8192):
                temp_file.write(chunk)

            # 获取文件名
            content_disposition = response.headers.get('Content-Disposition')
            filename = url.split('/')[-1]  # 默认使用URL的最后部分作为文件名
            if content_disposition:
                filename = content_disposition.split('filename=')[-1].strip('"')

            # 创建并返回UploadFile对象
            return UploadFile(filename=filename, file=open(temp_file.name, 'rb'))

    def create_metro_fault(self, fault_data: dict, report_file: UploadFile,
                           background_tasks: BackgroundTasks) -> MetroFault:
        new_fault = MetroFault(
            id=uuid4(),
            report="",
            **fault_data
        )
        equipment = EquipmentService.get_by_sfcode(new_fault.equipment_id)
        if equipment:
            print(equipment)
            new_fault.equipment_name = equipment.sfname

        metro_fault = MetroFaultDao.create_metro_fault(new_fault)
        if report_file:
            report_content = self.parse_report_file(report_file)
            background_tasks.add_task(self.update_metro_fault_report, metro_fault, report_content)

        return metro_fault

    def update_metro_fault_report(self, metro_fault, report_content):

        if report_content:
            content = f"设备是：{metro_fault.equipment_name}，文档内容是：{report_content}"
            print("-------content-------")
            print(content)
            parse_report_tool_id = ConfigDao.get_config_by_key("parse_report_tool_id").value
            equipment_name = metro_fault.equipment_name
            input_map = {
                "content": content,
                "equipment_name": equipment_name,
            }
            report_content = self.use_tool_by_id(parse_report_tool_id, "parse", 10, 1, input_map)
            print("-------report_content-----")
            print(report_content)
            metro_fault.report = report_content
            metro_fault = MetroFaultDao.update_metro_fault(metro_fault)
            # 生成task记录 可能多条。
            # 因为设备包含不同的策略，每个策略中周期 标准都不相同。
            # 所以要形成多个task分别计算。
            metro_fault_tasks = TaskService().create_tasks_by_metro_fault(metro_fault)
            if metro_fault_tasks is None:
                print("没有获取到设备标准，无法生成task推送")
                return
            need_post_task_list = []
            for task in metro_fault_tasks:
                print("执行获取建议")
                self.update_suggestion_by_task(task)
                if '无需调整' in task.suggestion:
                    print("当前建议为无需调整，不发送给大一平台")
                    continue
                need_post_task_list.append(task)

            try:
                metro_fault_tasks = TaskService().post_suggestion_by_task(metro_fault, need_post_task_list)
            except Exception as e:
                print(f"post_suggestion_by_task error: {e}")
            return metro_fault_tasks

    def parse_report_file(self, file: UploadFile) -> str:
        if file.filename.endswith('.pdf'):
            return self.extract_text_from_pdf(file)
        elif file.filename.endswith(('.doc', '.docx')):
            return self.parse_document(file)
        else:
            # return resp_500(message='当前仅支持PDF和DOCX格式的报告文件')
            raise ValueError("当前仅支持PDF和DOCX格式的报告文件")

    @staticmethod
    def get_metro_faults_by_equipment_id(equipment_id: str):
        """根据设备id查询故障记录"""
        metro_faults = MetroFaultDao.filter_metro_fault({'equipment_id': equipment_id})
        return metro_faults

    @staticmethod
    def update_metro_fault(metro_fault: MetroFault):
        """更新地铁故障记录"""
        return MetroFaultDao.update_metro_fault(metro_fault)

    @staticmethod
    def extract_text_from_pdf(file: UploadFile) -> str:
        """
        从上传的 PDF 文件中提取文本内容

        :param file: 上传的 PDF 文件对象 (UploadFile)
        :return: 提取的文本内容
        """

        try:
            # 使用 PyPDF2 读取 PDF
            pdf_reader = PdfReader(file.file)

            # 提取文本
            text = ""
            for page in pdf_reader.pages:
                text += page.extract_text() + "\n"

            return text.strip()
        except Exception as e:
            raise ValueError(f"处理 PDF 时出错: {str(e)}")

    @staticmethod
    def parse_document(file: UploadFile) -> Union[str, None]:
        try:
            # 获取文件扩展名
            file_extension = os.path.splitext(file.filename)[1]

            # 创建一个临时文件
            with tempfile.NamedTemporaryFile(delete=False, suffix=file_extension) as temp_file:
                # 读取上传的文件内容并写入临时文件
                file.file.seek(0)  # 确保从文件开头读取
                content = file.file.read()
                temp_file.write(content)
                temp_file_path = temp_file.name

            try:
                # 使用 textract 处理临时文件
                # text = textract.process(temp_file_path).decode('utf-8')
                # return text
                return None
            finally:
                # 确保删除临时文件
                os.unlink(temp_file_path)

        except Exception as e:
            print(f"解析文档时出错: {str(e)}")
            return None

    def get_importance_level(self, equipment_id: str):
        """根据行车、服务、安全、环境、范围、发生频次来计算后果影响程度"""
        # todo 由于当前设备ID能查询出多个策略中的设备。当前获取安全等级最高值。
        # 后续如果配置了策略编号 则返回最新task
        task_list = TaskService().get_tasks_by_equipment_id(equipment_id)
        # 是否能查出曾经地铁故障
        flag = False
        for task in task_list:
            metro_faults = MetroFaultDao.filter_metro_fault(
                {'equipment_id': equipment_id, 'start_time': task.suggestion_start_time,
                 "end_time": task.suggestion_end_time})
            if metro_faults:
                flag = True
                if len(metro_faults) > 1:
                    return LevelEnum.HARD.level_name
        if flag:
            levels = [self.get_level_by_metro_faults(equipment_id), LevelEnum.MEDIUM]
            return max(levels, key=lambda x: x.num, default=LevelEnum.EASY).level_name
        else:
            return self.get_level_by_metro_faults(equipment_id).level_name

    def get_level_by_metro_faults(self, equipment_id: str):
        """根据故障记录计算后果影响程度,不包含发生频次"""
        metro_faults = self.get_metro_faults_by_equipment_id(equipment_id)
        if metro_faults is None:
            return LevelEnum.EASY

        levels = []

        for metro_fault in metro_faults:
            levels.append(self.get_level_by_metro_fault(metro_fault))

        highest_level = max(levels, key=lambda x: x.num, default=LevelEnum.EASY)
        return highest_level

    def get_level_by_metro_fault(self, metro_fault):
        if metro_fault is None:
            return LevelEnum.EASY
        # 比较行车影响时间
        # 比较服务影响时间
        levels = [self.get_level_by_impact_operation_time(metro_fault.impact_operation_time),
                  self.get_level_by_impact_service_time(metro_fault.impact_service_time)]

        # 比较安全影响程度
        if metro_fault.safety_impact_level == '' or metro_fault.safety_impact_level == '不影响':
            levels.append(LevelEnum.EASY)
        elif metro_fault.safety_impact_level == '伤害':
            levels.append(LevelEnum.MEDIUM)
        elif metro_fault.safety_impact_level == '死亡':
            return LevelEnum.HARD
        # 比较环境影响程度
        if metro_fault.env_impact_level == '' or metro_fault.env_impact_level == '不影响':
            levels.append(LevelEnum.EASY)
        elif metro_fault.env_impact_level == '有影响':
            levels.append(LevelEnum.MEDIUM)
        elif metro_fault.env_impact_level == '影响大':
            return LevelEnum.HARD
        # 影响范围
        if metro_fault.incidence == '' or metro_fault.incidence == '部件本体':
            levels.append(LevelEnum.EASY)
        elif metro_fault.incidence == '设备本体、子系统':
            levels.append(LevelEnum.MEDIUM)
        elif metro_fault.incidence == '系统或车站、线路':
            return LevelEnum.HARD

        highest_level = max(levels, key=lambda x: x.num, default=LevelEnum.EASY)

        return highest_level

    @staticmethod
    def get_level_by_impact_operation_time(impact_operation_time):
        if isinstance(impact_operation_time, str):
            if impact_operation_time == '' or impact_operation_time == '不影响':
                return LevelEnum.EASY
            try:
                impact_operation_time = int(impact_operation_time)
            except ValueError:
                return LevelEnum.EASY  # 或者其他默认值

        if isinstance(impact_operation_time, (int, float)):
            if 0 < impact_operation_time < 5:
                return LevelEnum.MEDIUM
            elif impact_operation_time >= 5:
                return LevelEnum.HARD

        return LevelEnum.EASY  # 默认情况

    @staticmethod
    def get_level_by_impact_service_time(impact_service_time):
        if isinstance(impact_service_time, str):
            if impact_service_time == '':
                return LevelEnum.EASY
            try:
                impact_service_time = int(impact_service_time)
            except ValueError:
                return LevelEnum.EASY  # 或者其他默认值

        if isinstance(impact_service_time, (int, float)):

            if impact_service_time < 60:
                return LevelEnum.EASY
            elif 60 < impact_service_time < 480:
                return LevelEnum.MEDIUM
            elif 480 < impact_service_time:
                return LevelEnum.HARD

        return LevelEnum.EASY  # 默认情况

    @staticmethod
    def get_report_by_equipment_id(equipment_id: str):
        """根据设备id查询故障记录"""
        metro_faults = MetroFaultDao.filter_metro_fault({'equipment_id': equipment_id})
        equipment_name = metro_faults[0].equipment_name if metro_faults else None
        reports = list(dict.fromkeys(fault.report for fault in metro_faults))
        reports_string = "; ".join(reports)
        return reports_string, equipment_name

    def use_model_by_id(self, model_id, content, max_retries=5, delay=1):
        url = ConfigDao.get_config_by_key("use_model_url")
        headers = {
            'Content-Type': 'application/json'
        }

        suggestion_right_code_list = ConfigDao.get_config_by_key("suggestion_right_code_list").value
        print(suggestion_right_code_list)
        code_list = [code.strip() for code in suggestion_right_code_list.split(',')]

        for attempt in range(max_retries):
            # 生成当前时间戳
            current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

            # 在 content 后添加请求时间,解除缓存
            content_with_timestamp = f"{content.rstrip()} 请求时间：{current_time}"

            payload = json.dumps({
                "model": model_id,
                "messages": [
                    {
                        "role": "user",
                        "content": content_with_timestamp
                    }
                ],
                "stream": False
            })
            print("payload:", payload)

            try:
                response = requests.post(url.value, headers=headers, data=payload)
                print("response:", response.text)
                self.add_request_record(payload, response.text, url.value, response.status_code, "post")
                if response.status_code == 200:
                    response_content = self.get_content_from_response(response)
                    for code in code_list:
                        if code in response_content:
                            return response_content
                    if "无需调整" in response_content:
                        return response_content
                    print(f"响应内容不包含'{suggestion_right_code_list}'或'无需调整'，尝试次数：{attempt + 1}")
                else:
                    print(f"调用模型助手失败，状态码：{response.status_code}，尝试次数：{attempt + 1}")

                if attempt < max_retries - 1:
                    time.sleep(delay)
                continue
            except Exception as e:
                print(f"发生错误：{e}，尝试次数：{attempt + 1}")
                if attempt < max_retries - 1:
                    time.sleep(delay)
                continue

        print(f"调用模型助手失败，已尝试 {max_retries} 次")
        return ""

    def use_tool_by_id(self, tool_id: str, tool_type: str, max_retries: int = 5, delay: float = 1,
                       input_map=None):
        if input_map is None:
            input_map = {}
        print("use_tool_by_id")
        url = ConfigDao.get_config_by_key("use_tool_url").value
        headers = {
            "Content-Type": "application/json"
        }

        for attempt in range(max_retries):
            try:
                payload = self.prepare_payload(tool_type, input_map)

                payload = json.dumps(payload, ensure_ascii=False)
                response = requests.post(url + tool_id, headers=headers, data=payload.encode("utf-8"))
                print(response.text)

                self.add_request_record(payload, response.text, url + tool_id, response.status_code, "post")
                if response.status_code == 200:
                    response_data = response.json()
                    print(response_data)
                    flag = False
                    # 首先尝试获取 data.result.output
                    output = response_data['data']['result'].get('output')
                    # 如果 output 不存在，则尝试获取 data.result.result
                    if output is None:
                        flag = True
                        output = response_data['data']['result'].get('result')

                    print("output:", output)
                    if output:
                        try:

                            if flag:
                                output_dict = self.parse_json_from_markdown(output)
                                print(output)
                            else:
                                # 尝试解析 output 为 JSON
                                output_dict = json.loads(output)
                            code = output_dict.get('code')
                            result = output_dict.get('result')
                        except json.JSONDecodeError:
                            # 如果不是 JSON，使用原来的解析逻辑
                            output_parts = output.strip('"').split(',\n')
                            output_dict = {}
                            for part in output_parts:
                                if ':' in part:
                                    key, value = part.split(':', 1)
                                    output_dict[key.strip().strip('"')] = value.strip().strip('"')
                                else:
                                    # 处理没有冒号的情况
                                    output_dict['result'] = part.strip().strip('"')

                            code = output_dict.get('code')
                            result = output_dict.get('result')

                        print("code:", code)
                        print("result:", result)

                        try:
                            code = int(code) if code is not None else None
                        except ValueError:
                            print("code转换int失败")
                            code = None

                        if code == 0:
                            return result

                    # # output_json = self.parse_json_from_markdown(output)
                    # # print(output_json)
                    #
                    # if output_json.get('code') == 0:
                    #     return output_json.get('result', '')

                print(f"尝试 {attempt + 1} 失败，状态码: {response.status_code}, 响应: {response.text}")

            except Exception as e:
                print(f"尝试 {attempt + 1} 发生异常: {str(e)}")

            if attempt < max_retries - 1:
                time.sleep(delay)

        return ""

    @staticmethod
    def prepare_payload(tool_type: str, input_map: dict):
        print(input_map.get("equipment_name", ""))
        if tool_type == "suggestion":
            payload = {
                "inputs": {
                    "device": input_map.get("equipment_name", ""),
                    "content": input_map.get("suggestion_content", ""),
                    "original_content": input_map.get("original_content", ""),
                    "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                },
                "tweaks": {
                    "LLMChain-7143f": {},
                    "SystemMessagePromptTemplate-7e744": {},
                    "PromptTemplate-dc6ae": {},
                    "PromptTemplate-dbb91": {},
                    "HumanMessagePromptTemplate-23943": {},
                    "ChatPromptTemplate-7770a": {},
                    "ChatZhipuAI-f4236": {}
                }
            }
            return payload
        if tool_type == "parse":
            device = input_map.get("equipment_name", "")
            content = input_map.get("content", "")
            timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            return {
                "inputs": {
                    "input": {
                        "device": device,
                        "content": content,  # 使用当前的 content
                        "timestamp": timestamp
                    },
                },
                "tweaks": {
                    "ChatZhipuAI-3fada": {},
                    "LLMChain-892ac": {},
                    "PromptTemplate-5053d": {},
                    "SequentialChain-7d7f7": {},
                    "LLMChain-d416c": {},
                    "PromptTemplate-a3427": {},
                    "LLMChain-75b2d": {},
                    "PromptTemplate-b2bb8": {}
                }
            }
        return {}

    def post_tool_url(self, url, headers, payload):

        response = requests.post(url, headers=headers, data=payload)

        if response.status_code == 200:
            response_data = response.json()

            output = json.loads(response_data['data']['result']['output'])
            output_json = self.parse_json_from_markdown(output)

            print(output_json)
            if output_json.get('code') == 0:
                return output_json.get('result', ''), True

        return response, False

    @staticmethod
    def get_content_from_response(data_str):
        try:
            data = json.loads(data_str.text)
            choices = data.get('choices', [])

            if choices and choices[0].get('finish_reason') == 'stop':
                return choices[0].get('message', {}).get('content')
            else:
                print("The response is not complete or finish_reason is not 'stop'.")
                return None
        except (json.JSONDecodeError, KeyError, IndexError) as e:
            print(f"Error processing the response: {e}")
            return None

    def get_suggestion(self, task, max_retries=5, delay=1):
        content = f"设备编号是：{task.equipment_id}"
        get_suggestion_model_id = ConfigDao.get_config_by_key("get_suggestion_model_id").value
        suggestion_content = self.use_model_by_id(get_suggestion_model_id, content, max_retries, delay)
        print("1=====")
        print(suggestion_content)
        if "无需调整" in suggestion_content:
            return suggestion_content

        # 如果maintenance_rule中没有equipment_name从 equipment表中获取
        equipment = EquipmentService.get_by_sfcode(task.equipment_id)
        print("2=====")
        print(equipment)
        original_content = ""
        if task:
            original_content = task.maintenance_standard
        use_suggestion_tool_id = ConfigDao.get_config_by_key("use_suggestion_tool_id").value
        input_map = {
            "equipment_name": equipment.sfname,
            "suggestion_content": suggestion_content,
            "original_content": original_content
        }
        print("3=====")
        print(input_map)
        report_content = self.use_tool_by_id(use_suggestion_tool_id, "suggestion", max_retries, delay, input_map)
        print(report_content)
        return report_content

    def update_suggestion_by_task(self, metro_fault_task):
        content = self.get_suggestion(metro_fault_task, 10, 1)
        # print("ceshi")
        # content = "测试数据，测试建议，1-应该吧啦吧啦吧；2-应该吧啦吧啦吧啊阿啊阿"
        metro_fault_task.suggestion = content
        metro_fault_task.level = self.get_importance_level(metro_fault_task.equipment_id)
        metro_fault_task.status = StatusEnum.FINISHED.value
        MetroFaultTaskDao.update_self(metro_fault_task)
        return metro_fault_task

    # def push_suggestion(self,task_id, suggestion):
    #     task = MetroFaultTaskDao.get_task_by_id(task_id)
    #     task.suggestion = suggestion
    #     task.status = StatusEnum.FINISHED.value
    #     MetroFaultTaskDao.update_task(task)
    #     return task

    @staticmethod
    def parse_json_from_markdown(markdown_text):
        # 去除 Markdown 代码块标记和多余的空白字符
        json_str = re.sub(r'```json\s*|\s*```', '', markdown_text).strip()

        # 移除所有换行符和多余的空格，但保留字符串中的换行
        json_str = re.sub(r'\n(?=(?:[^"]*"[^"]*")*[^"]*$)', ' ', json_str)

        # 如果字符串以 "json" 开头，去掉它
        if json_str.lower().startswith('json'):
            json_str = json_str[4:].strip()

        # 将 Python 的 None 替换为 JSON 的 null
        json_str = json_str.replace('None', 'null')

        # 解析 JSON 字符串
        try:
            json_obj = json.loads(json_str)
            return json_obj
        except json.JSONDecodeError as e:
            print(f"JSON 解析错误: {e}")
            print(f"处理后的 JSON 字符串: {json_str}")
            return None

    @staticmethod
    def add_request_record(request_body, response_body, url, status_code, request_method):
        request_record = RequestRecord(request_body=request_body, response_body=response_body, request_url=url,
                                       response_code=status_code, request_method=request_method)
        RequestRecordDao.create_request_record(request_record)
        return request_record

    @staticmethod
    def add_alarm_record(data: dict):
        print(data)
        alarm_record = AlarmRecord(**data)
        alarm_record.message_list = json.dumps([item for item in data['message_list']], indent=2, default=str)
        alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(data.get("group_id"))
        if alarm_records:
            alarm_record.alarm_number = alarm_records[0].alarm_number
        else:
            alarm_record.alarm_number = AlarmRecordDao.get_alarm_number()
        AlarmRecordDao.create_alarm_record(alarm_record)
        return alarm_record

    @staticmethod
    def get_alarm_number():
        return AlarmRecordDao.get_alarm_number()

    @staticmethod
    def get_alarm_record_by_group_id(group_id):
        return AlarmRecordDao.get_alarm_record_by_group_id(group_id)

    @staticmethod
    def get_alarm_record_list_by_num(num):
        return AlarmRecordDao.get_alarm_record_list_by_num(num)

    @staticmethod
    def get_alarm_record_group_by_num(num):
        return AlarmRecordDao.get_alarm_record_group_by_num(num)


    def create_fault_work_order(self,group_id,chat_id):
        self.get_current_date()
        # alarm_record = AlarmRecordDao.get_alarm_record_by_id(alarm_record_id)
        alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(group_id)
        fault_work_order = FaultWorkOrderDao.get_fault_work_order_by_group_id_chat_id(group_id, chat_id)
        need_send_message_flag = False
        if fault_work_order is None:
            need_send_message_flag = True
            # 应该是查询出来。目前先写死
            fault_work_order = FaultWorkOrder(
                group_id=group_id,
                type='故障',
                child_type="故障",
                status="未处理",
                maintenance_department="18480400",
                repair_department="18480700",
                kind="变电",
                professional_kind="变电",
                fault_level="B级",
                self_inspection="非自检",
                chat_id = chat_id,
                plan_start_time=f"{self.data_time_value} 11:00:00",
                plan_end_time=f"{self.data_time_value} 14:30:00",
                code="1695100"
            )
            fault_work_order = FaultWorkOrderDao.create_fault_work_order(fault_work_order)
        fwo = fault_work_order.dict()
        fwo["alarm_record"] = [record.dict() for record in alarm_records]
        return fwo,need_send_message_flag


    def create_fault_work_ticket(self,group_id,chat_id):
        self.get_current_date()
        alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(group_id)
        fault_work_ticket = FaultWorkTicketDao.get_fault_work_ticket_by_group_id_chat_id(group_id, chat_id)
        if  fault_work_ticket is None:
            fault_work_ticket = FaultWorkTicket(
                group_id=group_id,
                chat_id=chat_id,
                work_address="抓马山车辆段牵混所",
                work_content="直流开关柜DDL故障处理",
                work_start_time=f"{self.data_time_value} 11:00:00",
                work_end_time=f"{self.data_time_value} 14:30:00",
                work_leader="李德志（四）",
                work_leader_phone="13560792616",
                work_member="李珂（四）",
                work_person_num=2,
                work_command="[2025](运营一中心-工电维保部)-(0408)-01",
                code="11Z2-05-01"
            )
            fault_work_ticket = FaultWorkTicketDao.create_fault_work_ticket(fault_work_ticket)
        fwt = fault_work_ticket.dict()
        fwt["alarm_record"] = [record.dict() for record in alarm_records]
        return fwt


    def create_fault_work_plan(self,group_id):
        self.get_current_date()
        alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(group_id)
        fault_work_plan = FaultWorkPlan(
            group_id=group_id,
            type="临时计划",
            construction_level="三级",
            work_kind="Z2",
            work_date=f"{self.data_time_value}",
            work_time="11:00-14:30",
            construction_manager="李德志",
            reporter="陈鑫17569113102",
            work_department="运营一中心-工电维保部-行车设备维保车间",
            phone="17569113102",
            work_content="直流开关柜DDL故障处理",
            station="抓马山车辆段牵混所",
            power_area="6DZ1/6DZ2/6DZ3/6DZ4/6DZ5/6DZ6/6DZ7停电",
            request_area="抓马山车辆段DCC",
            response_area="抓马山车辆段DCC",
            safeguards="1.穿戴安全防护用品 2.现场防护",
            declare_reason="故障处理",
            remark="1、作业前报6号线电环调度(电力)、场段运维调度2、作业期间抓马山车辆段牵混所整流机组、再生制动能量吸收装置退出运行,DC1500V停电;3、作业期间PSCADA刷出相应报文，供电智慧调度系统工作站刷出相应报文，场段综合监控工作站刷出相应报文",
            code="202503050038"
        )
        fault_work_plan = FaultWorkPlanDao.create_fault_work_plan(fault_work_plan)
        fwp = fault_work_plan.dict()
        fwp["alarm_record"] = [record.dict() for record in alarm_records]
        return fwp

    @staticmethod
    def create_fault_work_repair(group_id):
        alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(group_id)
        fault_work_repair = FaultWorkRepair(
            group_id=group_id,
            line="6号线",
            code="77880813"
        )
        fault_work_repair = FaultWorkRepairDao.create_fault_work_repair(fault_work_repair)
        fwr = fault_work_repair.dict()
        fwr["alarm_record"] = [record.dict() for record in alarm_records]
        return fwr

    @staticmethod
    def get_loop_diagrams_equipment(equipment_name, fault):
        equipment_infos = EquipmentInfoDao.get_equipment_info_by_equipment_name(equipment_name, fault)
        if not equipment_infos:
            return None
        return equipment_infos

    @staticmethod
    def get_all_loop_diagrams():
        return EquipmentInfoDao.get_all_loop_diagrams()

    @staticmethod
    def get_all_video():
        return EquipmentInfoDao.get_all_video()

    @staticmethod
    def add_equipment_info(data: dict):
        equipment_info = EquipmentInfo(**data)
        return EquipmentInfoDao.create_equipment_info(equipment_info)

    def get_order_or_ticket(self, get_id: str, get_type: str):
        return_dict = {}
        if get_type == "order":
            fwo = FaultWorkOrderDao.get_fault_work_order_by_id(get_id)
            if not fwo:
                return None
            return_dict = fwo.dict()
        elif get_type == "ticket":
            fwt = FaultWorkTicketDao.get_fault_work_ticket_by_id(get_id)
            if not fwt:
                return None
            return_dict = fwt.dict()
        elif get_type == "plan":
            fwp = FaultWorkPlanDao.get_fault_work_plan_by_id(get_id)
            if not fwp:
                return None
            return_dict = fwp.dict()
        elif get_type == "repair":
            fwr = FaultWorkRepairDao.get_fault_work_repair_by_id(get_id)
            if not fwr:
                return None
            return_dict = fwr.dict()
        else:
            return None

        if return_dict.get("group_id"):
            alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(return_dict.get("group_id"))
            return_dict['alarm_record'] = self.process_alarm_records(
                [alarm_record.dict() for alarm_record in alarm_records])

        if 'code' in return_dict:
            return_dict['sth_code'] = return_dict['code']
            del return_dict['code']

        # if return_dict.get("create_time"):
        #     return_dict['create_time'] = return_dict['create_time'].strftime("%Y-%m-%d")
        self.change_order_ticket_time(return_dict)

        return return_dict


    @staticmethod
    def change_order_ticket_time(return_dict):
        if return_dict is None:
            return None
        now = datetime.now()
        #order
        if return_dict.get("plan_start_time"):
            return_dict['plan_start_time'] = (now +timedelta(hours=2)).strftime("%Y-%m-%d %H:%M:%S")
        if return_dict.get("plan_end_time"):
            return_dict['plan_end_time'] = (now +timedelta(hours=4.5)).strftime("%Y-%m-%d %H:%M:%S")
        #plan
        if return_dict.get("work_date"):
            return_dict['work_date'] = now.strftime("%Y-%m-%d")
        #ticket
        if return_dict.get("work_start_time"):
            return_dict['work_start_time'] = (now +timedelta(hours=2)).strftime("%Y-%m-%d %H:%M:%S")
        if return_dict.get("work_end_time"):
            return_dict['work_end_time'] = (now +timedelta(hours=4.5)).strftime("%Y-%m-%d %H:%M:%S")
        if return_dict.get("create_time"):
            return_dict['create_time'] = now.strftime("%Y-%m-%d")


    @staticmethod
    def process_alarm_records(alarm_records):
        if not alarm_records:
            return {}  # 如果列表为空，返回空字典

        # 初始化结果字典，使用第一个记录的数据
        result = alarm_records[0].copy()  # 假设 alarm_record 是字典

        # 初始化 point 和 description 字段的集合
        points = set()
        descriptions = set()

        # 遍历所有记录，提取 point 和 description 字段并添加到集合中
        for record in alarm_records:
            points.add(record['point'])
            descriptions.add(record['description'])

        # 将集合转换回列表并更新结果字典
        result['point'] = list(points)
        result['description'] = list(descriptions)

        return result

    def create_fault_docx(self, group_id, type):
        if type == "order":
            fwo = self.create_fault_work_order(group_id)
            if fwo.get("group_id"):
                alarm_records = AlarmRecordDao.get_alarm_record_by_group_id(fwo.get("group_id"))
                fwo['alarm_record'] = self.process_alarm_records(
                    [alarm_record.dict() for alarm_record in alarm_records])

            path = ConfigDao.get_config_by_key("ticket_path").value
            output_path = self.process_file_and_upload_to_minio(path, self.create_dict_by_order(fwo))
            # path = r'1.docx'
            # output_path = self.fill_template(path, self.create_dict_by_order(fwo))
            return output_path

    @staticmethod
    def fill_template(template_path, context, prefix="filled_", suffix=""):
        # 获取模板文件的目录和文件名
        directory, filename = os.path.split(template_path)

        # 分离文件名和扩展名
        name, ext = os.path.splitext(filename)

        # 生成新的文件名
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        new_filename = f"{prefix}{name}_{timestamp}{suffix}{ext}"

        # 组合新的输出路径
        output_path = os.path.join(directory, new_filename)
        print("-----------------")
        template_path = r"1.docx"
        print(template_path)

        try:
            with zipfile.ZipFile(template_path, 'r') as zip_ref:
                print("ZIP structure:")
                for file in zip_ref.namelist():
                    print(file)
        except zipfile.BadZipFile:
            print("The file is not a valid ZIP file")

        # temp_file = '/tmp/temp_copy.docx'
        # shutil.copy2(template_path, temp_file)
        # doc = DocxTemplate(temp_file)

        # 渲染模板
        doc = DocxTemplate(template_path)
        doc.render(context)

        # 保存新文件
        doc.save(output_path)

        return output_path

    @staticmethod
    def create_dict_by_ticket(fwo: dict):
        points = []
        if fwo.get("alarm_record"):
            points = fwo["alarm_record"].get("point")
        context = {
            'work_address': fwo['work_address'],
            'work_start_time': fwo['work_start_time'],
            'work_leader': fwo['work_leader'],
            'work_member': fwo['work_member'],
            'work_command': fwo['work_command'],
            'work_content': fwo['work_content'],
            # ... 其他字段 ...

            # 添加 points 到 context
            'points': points,
            'points_str': ', '.join(points)  # 如果你想要一个字符串形式
        }
        return context

    @staticmethod
    def create_dict_by_order(fwo: dict):
        points = []
        if fwo.get("alarm_record"):
            points = fwo["alarm_record"].get("point")
        context = {
            'repair_department': fwo['repair_department'],
            # 'work_start_time': fwo['work_start_time'],
            # 'work_leader': fwo['work_leader'],
            # 'work_member': fwo['work_member'],
            # 'work_command': fwo['work_command'],
            # 'work_content': fwo['work_content'],
            # ... 其他字段 ...

            # 添加 points 到 context
            'points': points,
            'points_str': ', '.join(points)  # 如果你想要一个字符串形式
        }
        return context

    def process_file_and_upload_to_minio(self, url, context):
        """
        从URL下载文件，执行abc函数，将结果上传到MinIO，并返回下载链接。

        :param url: 要下载的文件的URL
        # :param abc_function: 处理文件的函数，接受输入和输出文件路径作为参数
        # :param minio_client: MinIO客户端实例
        # :param bucket_name: MinIO的桶名称
        :return: MinIO中文件的下载链接
        """
        try:
            # 下载文件到临时目录
            input_file = self.download_file_to_temp(url)
            if not input_file:
                return None

            print("in", input_file)

            output_file = self.fill_template(input_file, context)

            print("out", output_file)

            # 上传到MinIO
            object_name = os.path.basename(output_file)
            SimpleMinioClient().upload_file("jsj-bucket", object_name, output_file)

            # 生成下载链接
            download_url = SimpleMinioClient().generate_download_url("jsj-bucket", object_name)

            # 清理临时文件
            os.remove(input_file)
            os.remove(output_file)

            print(download_url)

            return download_url

        except Exception as e:
            print(f"处理过程中发生错误: {e}")
            return None

    @staticmethod
    def download_file_to_temp(url):
        """
        从给定的URL下载文件并保存到临时文件夹中。

        :param url: 要下载的文件的URL
        :return: 临时文件的路径
        """
        try:
            response = requests.get(url, stream=True)
            response.raise_for_status()

            file_name = os.path.basename(urlparse(url).path) or 'downloaded_file'
            with tempfile.NamedTemporaryFile(delete=False, suffix='_' + file_name) as temp_file:
                for chunk in response.iter_content(chunk_size=8192):
                    temp_file.write(chunk)
            return temp_file.name
        except requests.RequestException as e:
            print(f"下载文件时发生错误: {e}")
            return None

    @staticmethod
    def get_alarm_record_by_type(type: str, limit: int = None):
        return AlarmRecordDao.get_alarm_record_by_type(type,limit)

    @staticmethod
    def create_message(message: dict):
        alarm_record = AlarmRecord(**message)
        return AlarmRecordDao.create_message(alarm_record)

    @staticmethod
    def get_last_group_id():
        group_id = None

        # 首先检查 FaultWorkPlan
        fwp = FaultWorkPlanDao.get_lastest_plan()
        if fwp is not None:
            return fwp.group_id

        # 其次检查 FaultWorkOrder
        fwo = FaultWorkOrderDao.get_lastest_order()
        if fwo is not None:
            return fwo.group_id

        # 最后检查 FaultWorkTicket
        fwt = FaultWorkTicketDao.get_lastest_ticket()
        if fwt is not None:
            return fwt.group_id

        # 如果都没有找到，返回 None
        return group_id

    @staticmethod
    def get_agent_id_by_username(username: str):
        username_agent = ConfigDao.get_config_by_key("username_" + username)
        if username_agent is None:
            return ConfigDao.get_config_by_key("username_default").value
        return username_agent.value

    def get_question_str_first(self, global_group_id,chat_id):
        self.create_fault_work_ticket(global_group_id,chat_id)
        self.get_current_date()
        alarm_records = self.get_alarm_record_by_group_id(global_group_id)
        # 初始化 formatted_date 为默认值
        formatted_date = f"{self.data_time_value} 09:05:38"
        # 检查 alarm_records 是否有效
        if alarm_records and len(alarm_records) > 0:
            # 获取第一条记录的 alarm_time
            alarm_time = alarm_records[0].alarm_time
            if isinstance(alarm_time, datetime):  # 如果是 datetime 对象
                formatted_date = alarm_time.strftime("%Y-%m-%d %H:%M:%S")
            elif alarm_time is not None:  # 如果是字符串或其他类型
                formatted_date = alarm_time


        return self.get_order_ticket_plan_request(global_group_id,chat_id, "您好，变电班长通知您对于",
                                                  f",告警时间:{formatted_date} 针对故障", "")

    def get_question_str_secend(self, global_group_id,chat_id):
        return self.get_order_ticket_plan_request(global_group_id,chat_id, "您好，施工负责人，还有1小时您的", "",
                                                  "需要召开预想会:会议内容如下:\n (1)本次工作简述 \n (2)工具清单 \n (3)安全宣贯")

    def get_question_str_third(self, global_group_id,chat_id):
        now = datetime.now()
        self.get_current_date()
        # formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")
        # formatted_day = now.strftime("%Y-%m-%d")
        formatted_day = f"{self.data_time_value}"
        formatted_date = f"{self.data_time_value} 11:50:26"
        return self.get_order_ticket_plan_request(global_group_id,chat_id,
                                                  f"您好，您在{formatted_day}下发的故障工单，",
                                                  f"已有维修人员完成故障处置，完成时间:{formatted_date}，详细信息是",
                                                  "")

    def get_order_ticket_plan_request(self, group_id,chat_id, content1, content2, content3):
        fwo = FaultWorkOrderDao.get_fault_work_order_by_group_id_chat_id(group_id,chat_id)
        fwt = FaultWorkTicketDao.get_fault_work_ticket_by_group_id_chat_id(group_id,chat_id)
        # fwp = FaultWorkPlanDao.get_lastest_plan_by_group_id(group_id)
        alarm_records = self.get_alarm_record_by_group_id(group_id)
        alarm = self.process_alarm_records(
            [alarm_record.dict() for alarm_record in alarm_records])
        # , 告警时间: {alarm.get('alarm_time', '')}
        str1 = f"{content1}{alarm.get('station', '')}  {alarm.get('equipment', '')} {', '.join(alarm.get('description', ''))} 位置故障"
        str1 = str1 + f"{content2}"
        if fwo is not None:
            fwo = fwo.dict()
            str1 = str1 + f"维修工单为<a href='/ev/work-order?id={fwo['id']}&type=order' target='_blank' class='blank-link'>工单编号:{fwo['code']}</a>"
        if fwt is not None:
            fwt = fwt.dict()
            # str1 = str1 + f"工作票号：{fwt['code']}，请到<a href = 'https://www.baidu.com/'  target='_blank' class='blank-link'>工作票系统</a>查看"
            str1 = str1 + f"作业工作票为<a href='/ev/work-ticket?id={fwt['id']}&type=ticket' target='_blank' class='blank-link'>工作票:{fwt['code']}</a>"
        str1 = str1 + f"{content3}"
        return str1

    @staticmethod
    def upload_message_to_oss(group_id: str, messages: List[dict]) -> str:
        """
        将消息列表转换为 XLSX 文件，上传到 OSS，并返回下载链接。

        Args:
            group_id:  文件名 (不包含扩展名)。
            messages:  包含消息属性的字典列表。

        Returns:
            OSS 上的文件下载链接。 如果上传失败，则返回 None。
        """
        try:
            # 1. 创建 XLSX 文件 (在内存中)
            output = io.BytesIO()  # 使用 BytesIO 在内存中创建文件
            workbook = xlsxwriter.Workbook(output)
            worksheet = workbook.add_worksheet()

            # 定义固定的表头
            fixed_headers = ["序号", "事项时间", "报警等级", "事项描述", "状态"]

            # 写入表头
            for col_num, header in enumerate(fixed_headers):
                worksheet.write(0, col_num, header)

            # 写入数据
            for row_num, message in enumerate(messages):
                worksheet.write(row_num + 1, 0, row_num + 1)
                worksheet.write(row_num + 1, 1, message.get("alarm_time", ""))
                worksheet.write(row_num + 1, 2, message.get("alarm_level", ""))
                worksheet.write(row_num + 1, 3, message.get("description", ""))
                worksheet.write(row_num + 1, 4, message.get("status", ""))

            workbook.close()  # 确保 workbook 关闭，将数据写入 BytesIO

            # 2. 上传到 OSS
            filename = f"{group_id}.xlsx"  # 构建文件名
            object_name = "message_xlsx/" + filename  # OSS 上的对象名称 (可以包含路径)

            # 将 BytesIO 的内容上传到 OSS
            output.seek(0)  # 将文件指针重置到开头
            SimpleMinioClient().upload_file_by_io("jsj-bucket", object_name, output)

            # 3. 构建下载链接
            download_url = SimpleMinioClient().generate_download_url("jsj-bucket", object_name)

            return download_url

        except Exception as e:
            print(f"上传到 OSS 失败: {e}")
            return ""

    @staticmethod
    def get_schedule_record_by_date(date: str) -> List[ScheduleRecord]:
        return ScheduleRecordDao.get_schedule_record_by_date(date)

    # @staticmethod
    # def get_word_url(markdown_text):
    #
    #     # 将 Markdown 转换为 HTML
    #     html = markdown.markdown(markdown_text)
    #
    #     # 创建一个新的 Word 文档
    #     doc = Document()
    #
    #     # 定义标题样式
    #     for i in range(1, 7):
    #         style = doc.styles.add_style(f'Heading {i}', WD_STYLE_TYPE.PARAGRAPH)
    #         style.font.size = Pt(20 - i)  # 标题大小随级别递减
    #         style.font.bold = True
    #
    #     # 解析 HTML 并添加到 Word 文档
    #     lines = html.split('\n')
    #     for line in lines:
    #         if line.startswith('<h'):
    #             level = int(line[2])
    #             text = line[4:-5]  # 移除 HTML 标签
    #             doc.add_paragraph(text, style=f'Heading {level}')
    #         elif line.startswith('<p>'):
    #             text = line[3:-4]  # 移除 <p> 标签
    #             doc.add_paragraph(text)
    #         elif line.startswith('<ul>') or line.startswith('<ol>'):
    #             continue  # 忽略列表开始标签
    #         elif line.startswith('<li>'):
    #             text = line[4:-5]  # 移除 <li> 标签
    #             doc.add_paragraph(text, style='List Bullet')
    #         elif line == '</ul>' or line == '</ol>':
    #             continue  # 忽略列表结束标签
    #         else:
    #             doc.add_paragraph(line)
    #
    #     # 创建临时文件
    #     with tempfile.NamedTemporaryFile(delete=False, suffix='.docx') as tmp_file:
    #         temp_filename = tmp_file.name
    #         doc.save(temp_filename)
    #
    #     str_uuid = str(uuid.uuid4())
    #
    #     SimpleMinioClient().upload_file("jsj-bucket", f"故障报告{str_uuid}", temp_filename)
    #
    #     # 生成下载链接
    #     download_url = SimpleMinioClient().generate_download_url("jsj-bucket", str_uuid)
    #
    #     return download_url

    @staticmethod
    def update_alarm_status(chat_id: str ,status: str,update_time: str):
        if chat_id is None:
            return None
        alarm_status = AlarmStatusDao.get_by_chat_id(chat_id,status)
        if alarm_status is None:
            if update_time:
                update_time = datetime.strptime(update_time, "%Y-%m-%d %H:%M:%S")
            alarm_status = AlarmStatus(
                chat_id=chat_id,
                status=status,
                update_time=update_time
            )
            alarm_status = AlarmStatusDao.create_alarm_status(alarm_status)
        else:
            alarm_status = AlarmStatusDao.update_alarm_status(chat_id, status, update_time)

        return alarm_status

    @staticmethod
    def get_alarm_status_by_chat_id(chat_id: str):
        if chat_id is None:
            return None
        alarm_status_list = AlarmStatusDao.get_by_chat_id(chat_id,None)
        if alarm_status_list:
            for alarm_status in alarm_status_list:
                alarm_time = alarm_status.update_time
                if isinstance(alarm_time, datetime):  # 如果是 datetime 对象
                    # 将 datetime 对象格式化为字符串
                    alarm_status.update_time = alarm_time.strftime("%Y-%m-%d %H:%M:%S")

        return alarm_status_list


    def get_time_by_group_id(self,group_id:str,time_interval_min=180):
        alarm_records = self.get_alarm_record_by_group_id(group_id)
        # 初始化 formatted_date 为默认值
        # 检查 alarm_records 是否有效
        if alarm_records and len(alarm_records) > 0:
            # 获取第一条记录的 alarm_time
            alarm_time = alarm_records[0].alarm_time
            alarm_time = alarm_time + timedelta(minutes=time_interval_min)
            return alarm_time.strftime("%Y-%m-%d %H:%M:%S")
        return None

    def get_current_date(self):
        data_time = ConfigDao.get_config_by_key("data_time")
        if data_time:
            self.data_time_value = data_time.value
        pass




